﻿Partial Public Class Job

#Region "iCronTask"
    Private _CronGuid As New Guid
    Private _CronStatus As CronStatus = CronStatus.NotInitialized
    Private WithEvents pse As PSExecuter.PSExecute
    Private JobHistoryId As Int64
    Private ScriptHistoryId As Int64

    Public ReadOnly Property AllowConcurrent As Boolean Implements iCronTask.AllowConcurrent
        Get
            Return Not Me.SingleInstance
        End Get
    End Property

    Public ReadOnly Property CronGuid As Guid Implements iCronTask.CronGuid
        Get
            Return Me._CronGuid
        End Get
    End Property

    Public ReadOnly Property CronName As String Implements iCronTask.CronName
        Get
            Return String.Format("{0}:{1}", "Job", Me.Name)
        End Get
    End Property

    Public ReadOnly Property CronStatus As CronStatus Implements iCronTask.CronStatus
        Get
            Return Me._CronStatus
        End Get
    End Property

    Public Async Sub Execute() Implements iCronTask.Execute
        HistoryStart()
        For Each js As JobStep In Me.Steps.OrderBy(Function(x) x.StepNumber)
            If js.Parameters IsNot Nothing AndAlso js.Parameters.Count > 0 Then
                pse = New PSExecuter.PSExecute(js.Script.Code, js.Parameters, (Me.ForceStopOnError OrElse js.ForceStopOnError), (Me.ForceVerbose OrElse js.ForceVerbose), (Me.ForceDebug OrElse js.ForceDebug))
                StepStart(js.ScriptId, js.Parameters)
            Else
                pse = New PSExecuter.PSExecute(js.Script.Code, (Me.ForceStopOnError OrElse js.ForceStopOnError), (Me.ForceVerbose OrElse js.ForceVerbose), (Me.ForceDebug OrElse js.ForceDebug))
                StepStart(js.ScriptId)
            End If

            Try
                Await pse.ExecuteAsync()
            Catch ex As Exception
                Dim msg As String = String.Format("Error in {0}-{1} [{2}]:{3}{4}", Me.CronName, js.StepNumber, Me.CronGuid, Environment.NewLine, ex.ToString)
                Log.LogMessage(msg, LogTypes.Error)
            End Try
            StepEnd(js.ScriptId)
            If pse.Status = PSExecuter.PSExecute.PSExecuteStatus.Canceled OrElse
                (pse.Status = PSExecuter.PSExecute.PSExecuteStatus.Errored AndAlso
                 Not js.ContinueOnFailure) Then
                Me._CronStatus = pse.Status
                Exit For
            End If
        Next
        If Me._CronStatus = Service.CronStatus.Executing Then Me._CronStatus = Service.CronStatus.Completed
        HistoryEnd()
    End Sub

    Public Sub Cancel() Implements iCronTask.Cancel
        pse.CancelExecute()
    End Sub

    Public Event Canceled(sender As iCronTask) Implements iCronTask.Canceled
    Public Event Completed(sender As iCronTask) Implements iCronTask.Completed

    Private Sub HistoryStart()
        Using db As New PowerShellCronContext
            Dim jh As New JobHistory With {.CronGuid = Me.CronGuid,
                                            .JobId = Me.JobId,
                                            .StartDTO = DateTimeOffset.Now,
                                            .Status = "Executing"}
            db.JobHistories.Add(jh)
            db.SaveChanges()
            Me.JobHistoryId = jh.JobHistoryId
        End Using
        Me._CronStatus = Service.CronStatus.Executing
    End Sub
    Private Sub HistoryEnd()
        Using db As New PowerShellCronContext
            Dim jh As JobHistory = db.JobHistories.Find(Me.JobHistoryId)
            jh.EndDTO = DateTimeOffset.Now
            jh.Status = Me.CronStatus.ToString
            db.SaveChanges()
        End Using
    End Sub
    Private Sub StepStart(scriptId As Integer, Optional parameters As Hashtable = Nothing)
        Using db As New PowerShellCronContext
            Dim sh As New ScriptHistory With {.CronGuid = Me.CronGuid,
                                              .ScriptId = scriptId,
                                              .StartDTO = DateTimeOffset.Now,
                                              .Status = "Executing"}
            If Not IsNothing(parameters) Then sh.Parameters = parameters
            db.ScriptHistories.Add(sh)
            db.SaveChanges()
            Me.ScriptHistoryId = sh.ScriptHistoryId
        End Using
    End Sub
    Private Sub StepEnd(scriptId)
        Using db As New PowerShellCronContext
            Dim sh As ScriptHistory = db.ScriptHistories.Find(scriptId)
            sh.EndDTO = DateTimeOffset.Now
            sh.Status = Me.pse.Status.ToString
            db.SaveChanges()
        End Using
    End Sub
#End Region
    Public Sub UpdateNextRun()
        If Me.NextRun Is Nothing Then
            Throw New InvalidOperationException(String.Format("Cannot call UpdateNextRun when NextRun is nothing, either update NextRun or call UpdateNextRun with an initial value."))
        Else
            Call UpdateNextRun(Me.NextRun)
        End If
    End Sub
    Public Sub UpdateNextRun(initialValue As DateTimeOffset)
        Dim checkDTO As DateTimeOffset = initialValue
        While checkDTO <= DateTimeOffset.Now
            Select Case Me.Interval
                Case IntervalTypes.Custom
                    checkDTO = PSExecuter.PSExecute.Caculate(Of DateTimeOffset, DateTimeOffset)(Me.IntervalScript, Me.NextRun)
                Case IntervalTypes.Year
                    checkDTO = checkDTO.AddYears(Me.Interval)
                Case IntervalTypes.Month
                    checkDTO = checkDTO.AddMonths(Me.Interval)
                Case IntervalTypes.Week
                    checkDTO = checkDTO.AddDays(Me.Interval * 7)
                Case IntervalTypes.Day
                    checkDTO = checkDTO.AddDays(Me.Interval)
                Case IntervalTypes.Hour
                    checkDTO = checkDTO.AddHours(Me.Interval)
                Case IntervalTypes.Minute
                    checkDTO = checkDTO.AddMinutes(Me.Interval)
                Case IntervalTypes.Once
                    Me.NextRun = Nothing
                    Exit Sub
                Case Else
                    Me.NextRun = Nothing
                    Throw New NotImplementedException(String.Format("Interval Type [{0}] is not implemented!", Me.Interval.ToString))
            End Select

            If checkDTO <= Me.NextRun Then
                Me.NextRun = Nothing
                Throw New InvalidOperationException(String.Format("Generated a value in the past, {0}.", checkDTO.ToString))
            End If
        End While
        Me.NextRun = checkDTO
    End Sub

End Class
